package com.skymobi.stc.redis;

import com.google.common.base.Preconditions;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Transaction;

public class PoolInvocationHandler implements InvocationHandler {

	private static final Logger logger = LoggerFactory
			.getLogger(PoolInvocationHandler.class);
	private Jedis2Pool jedisPool;

	public PoolInvocationHandler(Jedis2Pool jedisPool) {
		this.jedisPool = null;
		Preconditions.checkArgument(jedisPool != null);
		this.jedisPool = jedisPool;
	}

	public Object invoke(Object proxy, Method method, Object args[])
			throws Throwable {
		Jedis2 jedis = (Jedis2) jedisPool.getResource();
		if (jedis == null) {
			throw new IllegalStateException("Cannot get redis connection.");
		}

		if (method.getName().equals("sync")) {
			try {
				sync((PipelineExecutor) args[0], jedis);
			} catch (Exception exception) {
				throw exception;
			} finally {
				jedisPool.returnResource(jedis);
			}
			return null;
		}

		if (method.getName().equals("syncAndReturnAll")) {
			try {
				return syncAndReturnAll((PipelineExecutor) args[0], jedis);
			} catch (Exception exception) {
				throw exception;
			} finally {
				jedisPool.returnResource(jedis);
			}
		}

		if (method.getName().equals("exec")) {
			try {
				return exec((TransactionExector) args[0], jedis);
			} catch (Exception exception) {
				throw exception;
			} finally {
				jedisPool.returnResource(jedis);
			}
		}

		Object value = null;

		try {
			value = method.invoke(jedis, args);
		} catch (Exception exception) {
			throw exception;
		} finally {
			jedisPool.returnResource(jedis);
		}

		logger.debug(
				"Execute jedis command[" + method.getName() + "], args["
						+ ArrayUtils.toString(args) + "], result["
						+ String.valueOf(value) + "]", jedis);

		return value;
	}

	private List<Object> exec(TransactionExector executor, Jedis2 jedis) {
		Transaction t = jedis.multi();
		executor.transacted(t);
		return t.exec();
	}

	private void sync(PipelineExecutor executor, Jedis2 jedis) {
		Pipeline p = jedis.pipelined();
		executor.pipelined(p);
		p.sync();
	}

	private List<Object> syncAndReturnAll(PipelineExecutor executor,
			Jedis2 jedis) {
		Pipeline p = jedis.pipelined();
		executor.pipelined(p);
		return p.syncAndReturnAll();
	}
}
